package library.router;

import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Configuration;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import library.router.module.IModuleLifeCycle;
import library.router.module.ModuleInfo;

/**
 * @author Created by qiang.hou on 2018/11/28.
 * @version 1.0
 */
public class ModuleRouter {
    /**
     * module file prefix
     */
    private static final String MODULE_PREFIX = "APP_Module_";
    /**
     * modules cache: name : ModuleInfo
     */
    private Map<String, ModuleInfo> modulesList = new HashMap<>();

    /**
     * IModuleLifeCycle cache: name : IModuleLifeCycle
     */
    private Map<String, IModuleLifeCycle> modulesLifeCycle = new HashMap<>();

    private ModuleRouter() {
    }

    private static class ModuleRouterHolder {
        private static ModuleRouter INSTANCE = new ModuleRouter();
    }

    public static ModuleRouter getInstance() {
        return ModuleRouterHolder.INSTANCE;
    }

    /**
     * load Module info from config xml in every Module
     *
     * @param context Context
     */
    public void loadModules(Context context) {
        AssetManager assetManager = context.getResources().getAssets();
        try {
            String[] fileList = assetManager.list("");
            if (null == fileList || fileList.length == 0) {
                return;
            }
            for (String fileName : fileList) {
                if (fileName.startsWith(MODULE_PREFIX)) {
                    // 解析json配置文件
                    ModuleInfo moduleInfo = parseModuleInfo(assetManager.open(fileName));
                    if (moduleInfo == null) {
                        continue;
                    }
                    modulesList.put(moduleInfo.moduleName, moduleInfo);
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据组件名调用生命周期
     *
     * @param moduleNames moduleName list
     */
    public void onModulesCreate(String[] moduleNames) {
        if (null == moduleNames) {
            return;
        }
        for (String moduleName : moduleNames) {
            if (!modulesLifeCycle.containsKey(moduleName) && modulesList.containsKey(moduleName)) {
                IModuleLifeCycle lifeCycle = getModuleLifeCycle(moduleName);
                if (null == lifeCycle) {
                    continue;
                }
                modulesLifeCycle.put(moduleName, lifeCycle);
            }

            if (modulesLifeCycle.containsKey(moduleName)) {
                modulesLifeCycle.get(moduleName).onCreate();
            }
        }
    }

    public void enterBackground(String[] moduleNames) {
        if (null == moduleNames) {
            return;
        }
        for (String moduleName : moduleNames) {
            if (!modulesLifeCycle.containsKey(moduleName) && modulesList.containsKey(moduleName)) {
                IModuleLifeCycle lifeCycle = getModuleLifeCycle(moduleName);
                if (null == lifeCycle) {
                    continue;
                }
                modulesLifeCycle.put(moduleName, lifeCycle);
            }

            if (modulesLifeCycle.containsKey(moduleName)) {
                modulesLifeCycle.get(moduleName).enterBackground();
            }
        }
    }

    public void enterForeground(String[] moduleNames) {
        if (null == moduleNames) {
            return;
        }
        for (String moduleName : moduleNames) {
            if (!modulesLifeCycle.containsKey(moduleName) && modulesList.containsKey(moduleName)) {
                IModuleLifeCycle lifeCycle = getModuleLifeCycle(moduleName);
                if (null == lifeCycle) {
                    continue;
                }
                modulesLifeCycle.put(moduleName, lifeCycle);
            }

            if (modulesLifeCycle.containsKey(moduleName)) {
                modulesLifeCycle.get(moduleName).enterForeground();
            }
        }
    }

    public void onModulesTerminate(String[] moduleNames) {
        if (null == moduleNames) {
            return;
        }
        for (String moduleName : moduleNames) {
            if (!modulesLifeCycle.containsKey(moduleName) && modulesList.containsKey(moduleName)) {
                IModuleLifeCycle lifeCycle = getModuleLifeCycle(moduleName);
                if (null == lifeCycle) {
                    continue;
                }
                modulesLifeCycle.put(moduleName, lifeCycle);
            }

            if (modulesLifeCycle.containsKey(moduleName)) {
                modulesLifeCycle.get(moduleName).onTerminate();
            }
        }
    }

    public void onModulesConfigurationChanged(String[] moduleNames, Configuration var1) {
        if (null == moduleNames) {
            return;
        }
        for (String moduleName : moduleNames) {
            if (!modulesLifeCycle.containsKey(moduleName) && modulesList.containsKey(moduleName)) {
                IModuleLifeCycle lifeCycle = getModuleLifeCycle(moduleName);
                if (null == lifeCycle) {
                    continue;
                }
                modulesLifeCycle.put(moduleName, lifeCycle);
            }

            if (modulesLifeCycle.containsKey(moduleName)) {
                modulesLifeCycle.get(moduleName).onConfigurationChanged(var1);
            }
        }
    }

    public void onModulesLowMemory(String[] moduleNames) {
        if (null == moduleNames) {
            return;
        }
        for (String moduleName : moduleNames) {
            if (!modulesLifeCycle.containsKey(moduleName) && modulesList.containsKey(moduleName)) {
                IModuleLifeCycle lifeCycle = getModuleLifeCycle(moduleName);
                if (null == lifeCycle) {
                    continue;
                }
                modulesLifeCycle.put(moduleName, lifeCycle);
            }

            if (modulesLifeCycle.containsKey(moduleName)) {
                modulesLifeCycle.get(moduleName).onLowMemory();
            }
        }
    }

    public void onModulesTrimMemory(String[] moduleNames, int level) {
        if (null == moduleNames) {
            return;
        }
        for (String moduleName : moduleNames) {
            if (!modulesLifeCycle.containsKey(moduleName) && modulesList.containsKey(moduleName)) {
                IModuleLifeCycle lifeCycle = getModuleLifeCycle(moduleName);
                if (null == lifeCycle) {
                    continue;
                }
                modulesLifeCycle.put(moduleName, lifeCycle);
            }

            if (modulesLifeCycle.containsKey(moduleName)) {
                modulesLifeCycle.get(moduleName).onTrimMemory(level);
            }
        }
    }

    private ModuleInfo parseModuleInfo(InputStream inputStream) {
        try {
            InputStreamReader isr = new InputStreamReader(inputStream, "UTF-8");
            BufferedReader br = new BufferedReader(isr);
            String line;
            StringBuilder builder = new StringBuilder();
            while ((line = br.readLine()) != null) {
                builder.append(line);
            }
            br.close();
            isr.close();

            JSONObject jsonObject = new JSONObject(builder.toString());
            ModuleInfo moduleInfo = new ModuleInfo();
            moduleInfo.moduleName = jsonObject.optString("moduleName");
            moduleInfo.packageName = jsonObject.optString("packageName");
            moduleInfo.delegateName = jsonObject.optString("delegateName");
            return moduleInfo;
        } catch (IOException | JSONException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * get a Object who implements IModuleLifeCycle by module name
     *
     * @param className module name
     * @return a Object implements IModuleLifeCycle
     */
    private IModuleLifeCycle getModuleLifeCycle(String className) {
        try {
            Class aClass = Class.forName(className);
            if (IModuleLifeCycle.class.isAssignableFrom(aClass) && !IModuleLifeCycle.class.equals(aClass) && !aClass.isInterface()) {
                return (IModuleLifeCycle) Class.forName(className).getConstructor().newInstance();
            }
        } catch (ClassNotFoundException
                | IllegalAccessException
                | InstantiationException
                | NoSuchMethodException
                | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}
